昨天,我們成功建立了一個 MainViewController.swift 檔案與其對應的 .xib 檔案。這個 .xib 檔案,就是我們今天的畫布。
今天,我們將在這塊「數位畫板」上,放置我們的第一個 UI 元件,並學習如何使用 Auto Layout 約束,讓它無論在哪個尺寸的 iPhone上,都能顯示在我們想要的位置。
我點開 MainViewController.xib,從元件庫隨便拖了一個 UILabel 到畫布的正中央。我心想,先在預設的 iPhone 16 Pro 模擬器上看看效果。按下 Command + R,App 啟動後,標籤完美地出現在螢幕中央。
接下來模擬器換成小尺寸的 iPhone SE,再次執行。
困惑點:在 iPhone SE 這麼小的螢幕上,標籤依然完美地保持在正中央!
這完全顛覆了我從一些舊資料中讀到的「版面會跑掉」的警告。這讓我陷入了更深的困惑:如果 Xcode 這麼聰明,那大家為什麼還要那麼辛苦地學習 Auto Layout?這種看不見的「魔法」可靠嗎?
AI 協作
我是一個 iOS 新手。我發現,我在 XIB 裡拖曳一個 UILabel 到畫面的正中央,沒有加上任何 Auto Layout 約束,但在不同尺寸的 iPhone 模擬器上運行時,它都能保持置中。這背後是什麼原理?如果它能自動運作,我為什麼還需要手動設定約束?請簡單說明
AI 的回答 :
您好!這是一個非常好的問題,也是許多 iOS 新手會遇到的共同疑惑。您觀察到的現象背後,其實牽涉到 iOS 介面佈局(Layout)系統的演進歷史。
簡單來說,您看到的「自動置中」是一種舊版佈局系統的預設行為,但它並不如您想像中那麼可靠和強大。
背後的原理:Autoresizing Mask
在 Auto Layout 成為主流之前,iOS 使用一種稱為 Frame-Based Layout 的佈局方式。開發者需要手動計算並設定每個視圖(View)的
frame屬性,也就是它的x、y座標、寬度和高度。為了讓這種寫死座標的方式能稍微適應不同尺寸的螢幕,蘋果引入了一個叫做 Autoresizing Mask(自動調整大小遮罩)的機制。
當您從 Interface Builder(XIB 或 Storyboard)中拖曳一個元件(如 UILabel)到畫面上,並且沒有為它添加任何 Auto Layout 約束時,Xcode 會預設啟用這個舊的 Autoresizing Mask 機制。
這個機制是如何運作的呢?
您可以把它想像成,您的 UILabel 被放在一個容器(父視圖)中,並透過一些「彈簧」和「支架」與容器的邊緣連接。
- 彈簧 (Flexible Margin/Size):可以被拉伸或壓縮。
- 支架 (Fixed Margin/Size):保持固定距離或大小。
當您把 UILabel 拖到畫面的正中央時,系統會「猜測」您的意圖,並設定一個如下的 Autoresizing Mask:
- 上下左右的邊距(Margin)都是彈簧。
- UILabel 本身的寬度和高度是支架(固定大小)。
當您在不同尺寸的 iPhone 上運行時:
- 螢幕變寬了:左右兩邊的「彈簧」會一起被拉長,因為它們是彈性的。由於兩邊拉長的長度相同,所以 UILabel 看起來仍然在水平方向的中央。
- 螢幕變高了:上下兩邊的「彈簧」會一起被拉長,所以 UILabel 看起來仍然在垂直方向的中央。
這就是您觀察到「它都能保持置中」的根本原因。這個行為是由一個重要的屬性
translatesAutoresizingMaskIntoConstraints控制的。當您不加任何約束時,這個屬性預設為true,系統就會在執行時期將這個 Autoresizing Mask「翻譯」成一組臨時的、隱含的 Auto Layout 約束,來模擬舊的行為。
為什麼還需要手動設定約束?
既然它能「自動」運作,為什麼我們還需要大費周章地去學習和設定 Auto Layout 約束呢?
因為 Autoresizing Mask 的「猜測」非常脆弱且功能有限,在真實世界的 App 開發中很快就會遇到瓶頸。
1. 它無法處理複雜的相對關係
Autoresizing Mask 只能定義一個視圖與其「父視圖」的關係。如果畫面上有兩個 UILabel,您希望它們之間永遠相距 10 點(points),Autoresizing Mask 完全辦不到。它不知道這兩個 UILabel 之間有任何關聯。
例子:您在置中的標籤旁放一個按鈕。當螢幕變寬時,標籤和按鈕會各自根據自己的「彈簧」移動,它們之間的距離會改變,甚至可能發生重疊。
2. 它在螢幕旋轉時經常會失效
從直向(Portrait)轉為橫向(Landscape)時,螢幕的寬高比會發生劇烈變化。Autoresizing Mask 那套簡單的「彈簧與支架」模型常常無法正確處理這種劇變,導致元件位置跑掉,不再是您預期的樣子。
3. 它無法適應內容的動態變化
如果您的 UILabel 因為多國語系(Localization)或使用者設定(Dynamic Type)而導致文字長度改變,您可能希望它能自動變寬,並推開旁邊的元件。Autoresizing Mask 做不到,它的寬高預設是固定的「支架」。
結論:從「猜測」到「定義」
您可以這樣理解兩者的差別:
- 預設行為 (Autoresizing Mask):您把元件放在一個大概的位置,然後跟系統說:「你幫我猜猜看,當螢幕變化的時候,它該怎麼動。」這在最簡單的情況下有效,但非常模糊且不可靠。
- 手動設定 Auto Layout 約束:您明確地告訴系統這個元件必須遵守的規則。這不是猜測,而是精確的定義。例如:
「我命令此標籤的水平中心」必須永遠等於「其父視圖的水平中心」。「我命令此標籤的垂直中心」必須永遠等於「其父視圖的垂直中心」。當您在 Interface Builder 中為一個視圖加上第一個手動約束時,Xcode 就會自動將它的
translatesAutoresizingMaskIntoConstraints屬性設為false,停用舊的 Autoresizing Mask 機制,並完全交由您定義的 Auto Layout 約束來決定佈局。總結來說,您看到的自動置中是 Autoresizing Mask 這個舊機制的「遺產」,它在簡單情境下提供了一種便利。但若要打造一個能夠適應各種螢幕尺寸、方向和內容變化的現代化、穩健的 iOS App,學習並使用 Auto Layout 來明確定義介面規則是不可或缺的專業技能。
現在,我要親手建立約束,讓元件的位置由我說了算。
我的操作步驟:
我回到 MainViewController.xib,按照 AI 的指示:
首先,點選畫布上的 UILabel。
接著,點擊 Xcode 編輯器右下角的「Align (對齊)」按鈕。
在跳出的選單中,我勾選了 Horizontally in Container 和 Vertically in Container。
最後,點擊 Add 2 Constraints。

@IBOutlet下一個問題:版面問題解決了。但如果我想用程式碼去改變這個 Label 的文字,該怎麼辦?
第一步:打開助理編輯器(按ctrl + option + command + enter)

第二步:按著 control 並拉元件 Label 到 MainViewController.swift 裡面,並取名為 lbTitle

第三步:改寫程式碼
class MainViewController: UIViewController {
// 透過 Control + 拖曳
@IBOutlet weak var lbTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// 透過 IBOutlet,用程式碼改變 Label 的文字
lbTitle.text = "Justin"
}
}
效果大概是長這樣 ⬇️
今天我們從一個看似正常的現象出發,透過與 AI 的對話,一路追根究柢,不僅學會了在 XIB 中設定 Auto Layout 約束、用 @IBOutlet 連結程式碼
明天,我們將探討 iOS 開發中最經典的架構模式:MVC (Model-View-Controller),為我們未來的專案打下穩固的架構基礎。